#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <linux/fb.h>
#include <pthread.h>
#include <signal.h>
#include <sys/time.h>
#include <pthread.h>

#include "../FIFO/fifo.h"
#include "carama.h"
#include "../msg.h"

extern struct kfifo *global_carama_fifo;       //全局变量fifo
static cam_buf_info buf_infos[FRAMEBUFFER_COUNT];   // 保存了摄像头的一些格式信息
static cam_fmt cam_fmts[FRAMEBUFFER_COUNT];         // mmap了一堆内存 用于存放捕捉到的信息
// 总感觉这样的全局变量不好 不如设置个指针然后操作指针去.. 先这样吧
static struct carama_info   carama;                 // 存储caram对应的信息

static int v4l2_dev_init(const char* device)
{
    carama.carama_fd = -1;
    struct v4l2_capability cap = {0};
    /* 打开摄像头 */
    carama.carama_fd = open(device, O_RDWR |O_NONBLOCK);
    if (0 > carama.carama_fd) {
        fprintf(stderr, "open error: %s: %s\n", device, strerror(errno));
        return -1;
    }
    printf("打开摄像头成功\r\n");
    /* 查询设备功能 */
    ioctl(carama.carama_fd, VIDIOC_QUERYCAP, &cap);
    /* 判断是否是视频采集设备 */
    if (!(V4L2_CAP_VIDEO_CAPTURE & cap.capabilities)) {
        fprintf(stderr, "Error: %s: No capture video device!\n", device);
        close(carama.carama_fd);
        return -1;
    }
    return 0;
}


static void v4l2_enum_format()
{
    struct v4l2_fmtdesc fmtdesc = {0};
    /* 枚举摄像头所支持的所有像素格式以及描述信息 */
    fmtdesc.index = 0;
    fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    // 从第0个依次列举
    while(0 == ioctl(carama.carama_fd,VIDIOC_ENUM_FMT, &fmtdesc)) {
        cam_fmts[fmtdesc.index].pixelformat = fmtdesc.pixelformat;
        strcpy((char*)cam_fmts[fmtdesc.index].description, (char*)fmtdesc.description);
        fmtdesc.index++;
    }
    // 将所有未使用的像素格式置为 0
    for (unsigned int i = fmtdesc.index; i < FRAMEBUFFER_COUNT; ++i) {
        cam_fmts[i].pixelformat = 0;
    }

}


//打印所有的参数 包括pixel_format 分辨率  帧数

void v4l2_print_format()
{
    // 前提是已经v4l2_enum_format了
    struct v4l2_frmsizeenum frmsize = {0};
    struct v4l2_frmivalenum frmival = {0};
    int i;
    // 先设定好type
    frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    frmival.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    for (i = 0; cam_fmts[i].pixelformat; i++) {
        // 某种格式
        printf("format<0x%x>, description<%s>\n", cam_fmts[i].pixelformat,
            cam_fmts[i].description);
        /* 枚举出摄像头所支持的所有视频采集分辨率 */
        frmsize.index = 0;
        frmsize.pixel_format = cam_fmts[i].pixelformat;
        frmival.pixel_format = cam_fmts[i].pixelformat;
        
        while (0 == ioctl(carama.carama_fd, VIDIOC_ENUM_FRAMESIZES, &frmsize)) {
            printf("size<%d*%d> ",
            frmsize.discrete.width,
            frmsize.discrete.height);
            frmsize.index++;
            // 所支持的帧率
            frmival.index = 0;
            frmival.width = frmsize.discrete.width;
            frmival.height = frmsize.discrete.height;

            while (0 == ioctl(carama.carama_fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival)) {
                printf("<%dfps>", frmival.discrete.denominator / frmival.discrete.numerator);
                frmival.index++;
            }
            printf("\n");
        }
        printf("\n");
    }
}

//carama也通过线程去控制 控制各个操作

//传入指针 因为ioctl可能会把这个值给改了
static int v4l2_set_format(unsigned int width,unsigned int height,unsigned int fps)
{
    // 设置完之后坑不会都按照自己想的来
    // 所以可以看看fmt 有没有被改 或者被改成啥了
    struct v4l2_format set_fmt = {0};      // 设定格式的时候用
    set_fmt.fmt.pix.height = height > CARAMA_MAX_HEIGHT ? CARAMA_MAX_HEIGHT : height;
    set_fmt.fmt.pix.width = width > CARAMA_MAX_WIDTH ? CARAMA_MAX_WIDTH : width;
    set_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//type 类型
    set_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    struct v4l2_streamparm streamparm = {0};
    if (0 > ioctl(carama.carama_fd, VIDIOC_S_FMT, &set_fmt)) {
        fprintf(stderr, "ioctl error: VIDIOC_S_FMT: %s\n", strerror(errno));
        return -1;
    }

     /*** 判断是否已经设置为我们要求的 RGB565 像素格式
     如果没有设置成功表示该设备不支持 RGB565 像素格式 */
     //不支持就算了先按照不支持的来

    //printf("视频帧大小<%d * %d> fps:<%d>\n", set_fmt.fmt.pix.width, set_fmt.fmt.pix.height,fps);
    carama.cur_format_height = set_fmt.fmt.pix.height;
    carama.cur_format_width = set_fmt.fmt.pix.width;
    /* 获取 streamparm */
    // 主要是看看支不支持设置视频的帧率
    ioctl(carama.carama_fd, VIDIOC_G_PARM, &streamparm);
    if (V4L2_CAP_TIMEPERFRAME & streamparm.parm.capture.capability) {
        streamparm.parm.capture.timeperframe.numerator = 1;
        streamparm.parm.capture.timeperframe.denominator = fps;//30fps
        if (0 > ioctl(carama.carama_fd, VIDIOC_S_PARM, &streamparm)) {
            fprintf(stderr, "ioctl error: VIDIOC_S_PARM: %s\n", strerror(errno));
            return -1;
        }
    }
    carama.fps = fps;
    long interval_ns = 1000000000 / carama.fps;
    carama.carama_timer.it_value.tv_sec = interval_ns / 1000000000;
    carama.carama_timer.it_value.tv_nsec = interval_ns % 1000000000;
    carama.carama_timer.it_interval = carama.carama_timer.it_value; // 周期性触发
    return 0;
}

void get_current_format(char * buf,unsigned int length)
{
    snprintf(buf,length,"width:%d,height:%d,fps:%d",carama.cur_format_width,carama.cur_format_height,carama.fps);
}


static int v4l2_init_buffer()
{
    struct v4l2_requestbuffers reqbuf = {0};
    struct v4l2_buffer buf = {0};

    /* 申请帧缓冲 */
    reqbuf.count = FRAMEBUFFER_COUNT;
    reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.memory = V4L2_MEMORY_MMAP;

    if (ioctl(carama.carama_fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
        fprintf(stderr, "ioctl error: VIDIOC_REQBUFS: %s\n", strerror(errno));
        return -1;
    }

    if (reqbuf.count < FRAMEBUFFER_COUNT) {
        fprintf(stderr, "Warning: only %d buffers allocated.\n", reqbuf.count);
    }

    // 建立内存映射
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;

    for (buf.index = 0; buf.index < reqbuf.count; buf.index++) {
        if (ioctl(carama.carama_fd, VIDIOC_QUERYBUF, &buf) < 0) {  // **修正: 传入 buf 而不是 reqbuf**
            fprintf(stderr, "ioctl error: VIDIOC_QUERYBUF: %s\n", strerror(errno));
            return -1;
        }

        buf_infos[buf.index].length = buf.length;
        buf_infos[buf.index].start = mmap(NULL, buf.length,
                        PROT_READ | PROT_WRITE, MAP_SHARED, carama.carama_fd, buf.m.offset);
        if (buf_infos[buf.index].start == MAP_FAILED) {
            perror("mmap error");
            return -1;
        }
    }

    /* 入队操作 */
    for (buf.index = 0; buf.index < reqbuf.count; buf.index++) {
        if (ioctl(carama.carama_fd, VIDIOC_QBUF, &buf) < 0) {
            fprintf(stderr, "ioctl error: VIDIOC_QBUF: %s\n", strerror(errno));
            return -1;
        }
    }

    return 0;
}

static  int v4l2_release_buffer() {
    struct v4l2_requestbuffers reqbuf = {0};

    // 解除内存映射
    for (int i = 0; i < FRAMEBUFFER_COUNT; i++) {
        if (buf_infos[i].start != MAP_FAILED) {
            if (munmap(buf_infos[i].start, buf_infos[i].length) == -1) {
                perror("munmap error");
                return -1;
            }
        }
    }

    // 释放帧缓冲
    reqbuf.count = 0; // 设置为0表示释放帧缓冲
    reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.memory = V4L2_MEMORY_MMAP;
    if (ioctl(carama.carama_fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
        fprintf(stderr, "ioctl error: VIDIOC_REQBUFS: %s\n", strerror(errno));
        return -1;
    }

    return 0;
}
// 开启视频流
static int v4l2_stream_on()
{
    /* 打开摄像头、摄像头开始采集数据 */
    // 枚举类型等于类型
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (0 > ioctl(carama.carama_fd, VIDIOC_STREAMON, &type)) {
        fprintf(stderr, "ioctl error: VIDIOC_STREAMON: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}
// Function to stop video streaming
static int v4l2_stream_off() {
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(carama.carama_fd, VIDIOC_STREAMOFF, &type) < 0) {
        perror("Error stopping video stream");
        return -1;
    }
    return 0;
}
// 超时处理函数
static void timer_handler(union sigval sv) {
   
    struct msg message = {0}; 
    message.type = CARAMA_MSG_TYPE;
    message.detail_msg.carama_msg = CARAMA_CAPTURE_FRAME;
    if (msgsnd(carama.msg_fd, &message, sizeof(message.detail_msg), IPC_NOWAIT) == -1) {
        perror("msgsnd LCD_FLUSH_READY error");
        return;
    }
}

int carama_init(const char * path) {
    carama.carama_fd = -1;
    if(v4l2_dev_init(path) == -1) {
        fprintf(stderr,"v4l2 open error");
        return -1; // 初始化fd
    }
    v4l2_enum_format();
    v4l2_print_format();
    printf("打印格式完成\r\n");
    if(v4l2_set_format(CARAMA_MAX_HEIGHT,CARAMA_MAX_WIDTH,30) == -1) {
        fprintf(stderr,"v4l2 set format error");
        close(carama.carama_fd);
        return -1;
    }
    printf("设定格式完成\r\n");
        // 分配buffer 准备视频采集
    if (v4l2_init_buffer() == -1) {
        fprintf(stderr,"v4l2_init_buffer error");
        close(carama.carama_fd);
        return -1;

    }
    // 开始视频采集
    if (v4l2_stream_on() == -1) {
        fprintf(stderr,"v4l2_stream_on error");
        close(carama.carama_fd);
        return -1;
    }
    printf("成功初始化buffer 开启视频流成功\r\n");
    return 0;
}


// 当得到一帧后怎么处理
void process_capture_frame()
{
    //printf("准备处理一帧\r\n");
    struct v4l2_buffer buf = {0};
    struct carama_msg send_msg;
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    int dq_buf = ioctl(carama.carama_fd,VIDIOC_DQBUF,&buf);
    //printf("调用timer_handler %d\r\n",dq_buf);
    if(dq_buf < 0 && errno != EWOULDBLOCK  && errno != EAGAIN) {
        fprintf(stderr, "dqbuf error: %s\n", strerror(errno));
        return;
    }
    if(dq_buf >= 0)
    {
        //printf("取得一帧成功width:%d,height:%d\r\n",carama.cur_format_width,carama.cur_format_height);
        send_msg.real_size = buf.bytesused;
        send_msg.height = carama.cur_format_height;
        send_msg.width = carama.cur_format_width;
        kfifo_write(global_carama_fifo,(void *)buf_infos[buf.index].start, &send_msg);
        //printf("最终写入fifo的大小是%d\r\n",send_msg.real_size);
        dq_buf = ioctl(carama.carama_fd, VIDIOC_QBUF, &buf);
        if(dq_buf < 0) {
            fprintf(stderr, "qbuf error: %s\n", strerror(errno));
            return;
        }
    }
    //printf("调用完毕\r\n");
    return;
}
// 处理暂停恢复
int process_pause(int state)
{
    if (state == 1) {
        // **恢复定时器，直接使用 carama.carama_timer 设定的初始值**
        timer_settime(carama.timerid, 0, &carama.carama_timer, NULL);
        printf("[Camera] 定时器恢复\n");
    } else {
        // **暂停定时器**
        struct itimerspec zero_timer = { {0, 0}, {0, 0} };
        timer_settime(carama.timerid, 0, &zero_timer, NULL);
        printf("[Camera] 定时器暂停\n");
    }
    return 0;
}


void disable_alarm()
{
    struct itimerspec zero_timer = { {0, 0}, {0, 0} };
    timer_settime(carama.timerid, 0, &zero_timer, NULL); // 停止定时器

    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGALRM);
    sigprocmask(SIG_BLOCK, &set, NULL); // 屏蔽 SIGALRM

    sigset_t pending;
    sigpending(&pending);
    if (sigismember(&pending, SIGALRM)) {
        int sig;
        printf("[Camera] 清除所有未决 SIGALRM\n");
        sigwait(&set, &sig);  // 清除可能的信号
    }
    printf("[Camera] 定时器已禁用\n");
}

void enable_alarm()
{
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGALRM);
    sigprocmask(SIG_UNBLOCK, &set, NULL); // 解除 SIGALRM 屏蔽
    timer_settime(carama.timerid, 0, &carama.carama_timer, NULL);
    printf("[Camera] 定时器恢复运行\n");
}
// 处理格式转换
void process_format_change(unsigned int width, unsigned int height,unsigned int fps)
{
    disable_alarm(); // 暂停 alarm 信号处理
    v4l2_stream_off();
    v4l2_release_buffer();
    v4l2_set_format(width,height,fps);
    v4l2_init_buffer();
    v4l2_stream_on();
    // 恢复对alarm信号的响应 同时重新设置定时器的值(根据carama.fps)
    enable_alarm();

}
void* carama_thread(void * parma)
{
    static int pre_carama_state = 0;
    carama.msg_fd = *(int *)parma;
    printf("carama外设初始化完毕\r\n");
    // 接收消息队列中的消息
    struct msg message;
    struct sigevent sev;
    // 配置定时器
    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_notify_function = timer_handler;
    sev.sigev_value.sival_ptr = NULL; // 传递线程 ID
    sev.sigev_notify_attributes = NULL;

    // 创建定时器
    if (timer_create(CLOCK_MONOTONIC, &sev, &carama.timerid) != 0) {
        perror("timer_create 失败");
        pthread_exit(NULL);
    }

    // 计算超时时间： 1 秒 / FPS
    long interval_ns = 1000000000 / carama.fps;
    carama.carama_timer.it_value.tv_sec = interval_ns / 1000000000;
    carama.carama_timer.it_value.tv_nsec = interval_ns % 1000000000;
    carama.carama_timer.it_interval = carama.carama_timer.it_value; // 周期性触发

    if (timer_settime(carama.timerid, 0, &carama.carama_timer, NULL) != 0) {
        perror("timer_settime 失败");
        pthread_exit(NULL);
    }

    int count = 0;
    while (1) {
       if (msgrcv(carama.msg_fd, &message, sizeof(message.detail_msg), CARAMA_MSG_TYPE, 0) == -1) {
           perror("msgrcv error");
           continue;
       }
       switch (message.detail_msg.carama_msg) {
           case CARAMA_CAPTURE_FRAME:
               // 处理摄像头消息
                process_capture_frame();
                count++;
                // if(count == 1000) {
                //     struct msg message = {0}; 
                //     message.type = CARAMA_MSG_TYPE;
                //     message.detail_msg.carama_msg = CARAMA_FROMAT_CHANGE;
                //     msgsnd(carama.msg_fd, &message, sizeof(message.detail_msg), IPC_NOWAIT);
                // }
               break;
           case CARAMA_STATE_CHANGE:
               // 处理LED消息
               printf("摄像头暂停采集了\r\n");
               process_pause(pre_carama_state);
               pre_carama_state = !pre_carama_state;
               break;
           case CARAMA_FROMAT_CHANGE:
               printf("准备更新格式\r\n");
               int width = message.detail_msg.msg_buffer[0] + message.detail_msg.msg_buffer[1] * 255;
               int height = message.detail_msg.msg_buffer[2] + message.detail_msg.msg_buffer[3] * 255;
               int fps = message.detail_msg.msg_buffer[4] + message.detail_msg.msg_buffer[5] * 255;
               process_format_change(width, height,fps);
               break;
           default:
               fprintf(stderr, "Unknown message type\n");
               break;
       }
   }
   pthread_exit(NULL);
}


// int main()
// {
//     // 创建消息队列
//     int msg_key = msgget(12345, IPC_CREAT | 0666);
//     if (msg_key == -1) {
//         perror("msgget error");
//         return -1;
//     }
//     // 放入lcd的CacheBuffer中
//     pthread_t carama_tid;
//     if (pthread_create(&carama_tid, NULL, carama_thread, &msg_key) != 0) {
//         perror("pthread_create error");
//         msgctl(msg_key, IPC_RMID, NULL); // 删除消息队列
//         return -1;
//     }
//     //等待线程结束
//     pthread_join(carama_tid, NULL);
//     // 删除消息队列
//     msgctl(msg_key, IPC_RMID, NULL);
//     return 0;
// }